import threading
import time
import traceback
import subprocess
import datetime
import inspect
import os
import sys

g_NodeIndex = 0

class ThreadLinkNode:

    def __init__(self, src, val):
        global g_NodeIndex
        g_NodeIndex += 1
        self._src = src
        self._idx = g_NodeIndex
        self._val = val
        self._next = None

"""
run in thread
"""
class ThreadLogic:

    def __init__(self):
        self._running = False
        self._mainThreadReadNode = ThreadLinkNode("", None)       # main thread read this 
        self._mainThreadWriteNode = ThreadLinkNode("", None)      # main thread write this
        self._threadWriteNode = self._mainThreadReadNode      # thread write msg
        self._threadReadNode = self._mainThreadWriteNode      # thread read this
        self._hookOnStarted = None

    def run(self):
        if self._hookOnStarted:
            self._hookOnStarted()
        self.onStarted()
        self.threadLoop()

    def threadLoop(self):
        while self._running:
            self.threadFrame()
            time.sleep(0.001)

    def threadFrame(self):
        node = self.popFromMainThread()
        while node:
            try:
                self.handle(*node._val)
            except:
                traceback.print_exc()
            node = self.popFromMainThread()

    def onStarted(self):
        pass

    def handle(self, op, *args):
        if "TimeoutCallback" == op:
            func = args[0]
            if func:
                func()
        else:
            self.onHandle(op, *args)

    def onHandle(self, op, *args):
        pass

"""
run in main thread
"""
class ThreadBase(ThreadLogic):

    _name = "noname"

    def __init__(self):
        super().__init__()
        self._thread = threading.Thread(target=self.run)

    def setThreadProperty(self, k: str, v: any):
        setattr(self._thread, f"P_{k}", v)

    def getThreadProperty(self, k, defaultvalue: any = None):
        return getattr(self._thread, f"P_{k}", defaultvalue)

    def start(self):
        self._running = True
        self._thread.start()

    def stop(self):
        self._running = False

    def frame(self):
        node = self.popFromThread()
        while node:
            try:
                self.mainHandle(*node._val)
            except:
                traceback.print_exc()
            node = self.popFromMainThread()

    def mainHandle(self, *args):
        print(f"main handle: {args}")
        op = args[0]
        if "ExecuteFunction" == op:
            func = args[1]
            func()
        else:
            self.onMainHandle(*args)

    def onMainHandle(self, *args):
        pass

    """
    [write] main thread -> thread
    """
    def pushToThread(self, src: str, msg: any):
        newnode = ThreadLinkNode(src, msg)
        if self._mainThreadWriteNode._next:
            node = self._mainThreadWriteNode._next
            self._mainThreadWriteNode = node
            node._next = newnode
        else:
            self._mainThreadWriteNode._next = newnode

    """
    [read] main thread <- thread
    """
    def popFromThread(self):
        if self._mainThreadReadNode._next:
            node = self._mainThreadReadNode._next
            self._mainThreadReadNode = node
            return node
        else:
            return None
    
    """
    [write] thread -> main thread
    """
    def pushToMainThread(self, msg: any):
        curname = GetCurrentThreadName()
        newnode = ThreadLinkNode(curname, msg)
        if self._threadWriteNode._next:
            node = self._threadWriteNode._next
            self._threadWriteNode = node
            node._next = newnode
        else:
            self._threadWriteNode._next = newnode
    
    """
    [read] thread <- main thread
    """
    def popFromMainThread(self):
        if self._threadReadNode._next:
            node = self._threadReadNode._next
            self._threadReadNode = node
            return node
        else:
            return None

def GetCurrentThreadName():
    import threading
    obj = threading.current_thread()
    return getattr(obj, "P_ThreadName", "UnkonwThreadName")

def execute_command(command, timeout=None, shell=False, cwd=None, env=None):
    """
    执行系统命令并返回输出结果
    
    参数:
        command (str or list): 要执行的命令，字符串或列表形式
        timeout (int): 超时时间(秒)，默认无超时
        shell (bool): 是否通过shell执行，默认为False
        cwd (str): 执行命令的工作目录
        env (dict): 环境变量字典
    
    返回:
        dict: 包含以下键的字典
            - 'success': 命令是否成功执行(布尔值)
            - 'returncode': 命令的返回码
            - 'output': 命令的标准输出
            - 'error': 命令的错误输出
            - 'exception': 异常信息(如果有)
    
    示例:
        >>> result = execute_command('ls -l')
        >>> print(result['output'])
    """
    result = {
        "cmd": command,
        'success': False,
        'returncode': None,
        'output': '',
        'error': '',
        'exception': None
    }
    
    try:
        process = subprocess.Popen(
            command,
            shell=shell,
            cwd=cwd,
            env=env,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,
            text=True,
            encoding="utf-8"
        )
        
        stdout, stderr = process.communicate(timeout=timeout)
        if not stdout: stdout = ""
        if not stderr: stderr = ""
        
        result['returncode'] = process.returncode
        result['output'] = stdout.strip()
        result['error'] = stderr.strip()
        result['success'] = (process.returncode == 0)
        
    except subprocess.TimeoutExpired:
        process.kill()
        result['error'] = f"Command timed out after {timeout} seconds"
        result['exception'] = 'TimeoutExpired'
        
    except Exception as e:
        result['error'] = str(e)
        result['exception'] = type(e).__name__
        
    return result

def GetTimeStamp():
    """
    获取适合文件命名的时间戳
    返回格式: 20231225_143045
    """
    return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

g_DebugPrintHook = None

def DebugPrint(*args, **kwargs):
    """增强版打印函数，显示调用位置信息（包含类名）"""
    stack = inspect.stack()
    try:
        # 获取调用者信息
        caller_frame = stack[1]
        
        # 获取基础信息
        function_name = caller_frame.function
        file_name = os.path.basename(caller_frame.filename)
        line_number = caller_frame.lineno
        
        # 尝试获取类名
        class_name = None
        frame_locals = caller_frame.frame.f_locals
        
        # 检查实例方法（有'self'参数）
        if 'self' in frame_locals:
            self_obj = frame_locals['self']
            class_name = type(self_obj).__name__
        
        # 检查类方法（有'cls'参数）
        elif 'cls' in frame_locals:
            cls_obj = frame_locals['cls']
            if hasattr(cls_obj, '__name__'):  # 确保是类对象
                class_name = cls_obj.__name__
        
        # 构建完整函数名（包含类名前缀）
        full_function_name = f"{class_name}.{function_name}" if class_name else function_name
        
        # 构建消息
        sep = kwargs.pop('sep', ' ')
        end = kwargs.pop('end', '\n')
        message = sep.join(map(str, args))
        suffix = f" from '{full_function_name}' in {file_name}:{line_number}行"
        
        # 输出到指定流
        output_stream = kwargs.pop('file', sys.stdout)
        txt = f"[{GetTimeStamp()}] {message}{suffix}"
        print(txt, end=end, file=output_stream, **kwargs)
        if g_DebugPrintHook:
            g_DebugPrintHook(txt)
    
    finally:
        # 显式清除栈帧引用，避免内存泄漏
        del stack

import builtins
builtins.DebugPrint = DebugPrint
